<!doctype html>
<html lang="zh-CN">
<head>

    <meta charset="utf-8">
    <meta name="generator" content="Hugo 0.74.1" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <title>Github action 的开发到发布 | caijinglong的博客</title>
    <meta property="og:title" content="Github action 的开发到发布 - caijinglong的博客">
    <meta property="og:type" content="article">
        
    <meta property="article:published_time" content="2020-09-07T16:49:35&#43;08:00">
        
        
    <meta property="article:modified_time" content="2020-09-07T16:49:35&#43;08:00">
        
    <meta name="Keywords" content="golang,go语言,flutter,caijinglong,java,android,博客,项目管理,python,软件架构,公众号,小程序">
    <meta name="description" content="Github action 的开发到发布">
        
    <meta name="author" content="caijinglong">
    <meta property="og:url" content="http://www.kikt.top/posts/github/actions/create/">
    <link rel="shortcut icon" href="/favicon.ico" type="image/x-icon">

    <link rel="stylesheet" href="/css/normalize.css">
    
        <link rel="stylesheet" href="/css/prism.css">
    
    <link rel="stylesheet" href="/css/style.css">
    <script type="text/javascript" src="//cdn.bootcss.com/jquery/3.2.1/jquery.min.js"></script>

    

   
    <script>
        (function(){
            var bp = document.createElement('script');
            var curProtocol = window.location.protocol.split(':')[0];
            if (curProtocol === 'https') {
                bp.src = 'https://zz.bdstatic.com/linksubmit/push.js';
            }
            else {
                bp.src = 'http://push.zhanzhang.baidu.com/push.js';
            }
            var s = document.getElementsByTagName("script")[0];
            s.parentNode.insertBefore(bp, s);
        })();
    </script>

    
    
</head>

<body>
<header id="header" class="clearfix">
    <div class="container">
        <div class="col-group">
            <div class="site-name ">
                
                    <a id="logo" href="http://www.kikt.top">
                        caijinglong的博客
                    </a>
                
                <p class="description">Android/Flutter开发者，对于golang/python/ios/java 均有所了解</p>
            </div>
            <div>
                <nav id="nav-menu" class="clearfix">
                    <a class="" href="http://www.kikt.top">首页</a>
                    
                    <a  href="http://www.kikt.top/archives/" title="归档">归档</a>
                    
                    <a  href="http://www.kikt.top/contact/" title="接单">接单</a>
                    
                    <a  href="http://www.kikt.top/about/" title="关于">关于</a>
                    
                </nav>
            </div>
        </div>
    </div>
</header>


<div id="body">
    <div class="container">
        <div class="col-group">

            <div class="col-8" id="main">
                <div class="res-cons">
                    <article class="post">
                        <header>
                            <h1 class="post-title">Github action 的开发到发布</h1>
                        </header>
                        <date class="post-meta meta-date">
                            2020年9月7日
                        </date>
                        
                        <div class="post-meta meta-category">
                            |
                            
                                <a href="http://www.kikt.top/categories/github">github</a>
                            
                        </div>
                        
                        
                        <div class="post-meta">
                            <span id="busuanzi_container_page_pv">|<span id="busuanzi_value_page_pv"></span><span> 阅读</span></span>
                        </div>
                        
                        <div class="post-content">
                            <p>Github action 这东西是好东西, 但我看了一下, 很多朋友都是停留在用的阶段, 其实偶尔也要换换口味, 自己开发一个 action, 而不是仅仅是用</p>
<h2 id="简介">简介</h2>
<p>github actions 是 github 推出的一个工作流的工具, 目的是为了帮助我们在某些情况下主动触发仓库的动作, 从而完成 单元测试/CI/CD, 甚至包括 release,发布包管理工具等等</p>
<p>官方关于 actions 有关的一些仓库都在这里: <a href="https://github.com/actions">https://github.com/actions</a> , <a href="https://docs.github.com/en/actions">文档在这里</a></p>
<p>github 的主语言是 js, 当然也肯定也支持 ts</p>
<p>另外如果对于速度需求并不高的朋友, 也可以使用 docker, 但因为 docker 安装的过程会根据镜像大小有一定的耗时, 所以不一定适用于所有朋友</p>
<p>如果，你对于本文章不是很感兴趣，可以参考<a href="https://docs.github.com/en/actions/creating-actions">创建 action 的文档</a></p>
<h2 id="新建">新建</h2>
<p>因为我对于 js 比较不喜欢, 所以使用 ts(虽然也不是很感冒, 但是会好一点)</p>
<p>进入这个<a href="https://github.com/actions/typescript-action">仓库</a>, 然后使用<img src="https://cdn.jsdelivr.net/gh/kikt-blog/blog-2@t1/uPic/1599469136.png" alt="image-20200907165856148">按钮, 完成初始化的过程.</p>
<p><!-- raw HTML omitted --></p>
<p>这里我们创建一个仓库, 这个仓库的目的是自动给 issue 打上 label</p>
<h3 id="初始化后的仓库">初始化后的仓库</h3>
<p><!-- raw HTML omitted --></p>
<p>简单介绍一下这个仓库, 有一些文件和注意事项</p>
<ul>
<li>action.yml 是 action 本身的配置文件(别的项目实际就是读取这个东西来确定入口在哪里), 包括参数的配置都是这东西</li>
<li>一个标准的 npm 项目, 指定了入口</li>
<li>src 内是主要的 ts 代码</li>
<li>ts 代码需要被编译为 js 才能使用</li>
<li>dist 内就是编译产物, git 的版本控制需要包含 dist 下的所有文件, 不然运行的时候会是老代码</li>
<li>项目本身自带 action, 主要是 CI 这个项目的</li>
</ul>
<h2 id="入门">入门</h2>
<h3 id="开发环境">开发环境</h3>
<ul>
<li>vscode, 我这里是使用 vscode 进行编辑, 你请根据自己的情况</li>
<li>npm(node), 我是使用 nvm 管理的</li>
</ul>
<p>如果你的 node 大于 12.0, 理论上不用动</p>
<h3 id="clone-项目">clone 项目</h3>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-bash" data-lang="bash">git clone https://github.com/CaiJingLong/action_auto_label.git
cd action_auto_label
npm i
</code></pre></div><h3 id="官方支持库">官方支持库</h3>
<p><a href="https://github.com/actions/toolkit">toolkit</a>包含了 github 官方支持的一些库, 就不一一介绍了</p>
<ul>
<li><a href="https://github.com/actions/toolkit/blob/main/packages/core">@actions/core</a> actions 的核心库, 会被默认包含</li>
<li><a href="https://github.com/actions/toolkit/blob/main/packages/exec">@actions/exec</a> 如果你需要执行 cli 工具, 比如 ls, mkdir, 之类的操作, 可以用这个, 可以便利的封装过程和日志输出之类的东西</li>
<li><a href="https://github.com/actions/toolkit/blob/main/packages/io">@actions/io</a> glob 匹配文件, 我们都知道 <code>ls *.sh</code> 这样的东西, 这个*就是 glob, 而不是正则</li>
<li><a href="https://github.com/actions/toolkit/blob/main/packages/github">@actions/github</a> github 的封装, 这东西就包含了操作 github 本身的操作</li>
</ul>
<p>因为本篇要操作 github, 所以我们把这个东西加入以下</p>
<p><code>npm i @actions/github</code></p>
<h3 id="hello-world">Hello world</h3>
<p>这里要注意, ts 中不建议我们使用<code>console.log</code>来输出日志, 所以我们这里使用<code>core.info</code>方法来输出</p>
<p>老规矩, 先 hello world 一下.</p>
<p>src/main.ts</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-ts" data-lang="ts"><span style="color:#66d9ef">import</span> <span style="color:#f92672">*</span> <span style="color:#66d9ef">as</span> <span style="color:#a6e22e">core</span> <span style="color:#a6e22e">from</span> <span style="color:#e6db74">&#34;@actions/core&#34;</span>;

<span style="color:#a6e22e">async</span> <span style="color:#66d9ef">function</span> <span style="color:#a6e22e">run</span>()<span style="color:#f92672">:</span> <span style="color:#a6e22e">Promise</span>&lt;<span style="color:#f92672">void</span>&gt; {
  <span style="color:#66d9ef">try</span> {
    <span style="color:#a6e22e">core</span>.<span style="color:#a6e22e">info</span>(<span style="color:#e6db74">`Hello world`</span>);
  } <span style="color:#66d9ef">catch</span> (<span style="color:#a6e22e">error</span>) {
    <span style="color:#a6e22e">core</span>.<span style="color:#a6e22e">setFailed</span>(<span style="color:#a6e22e">error</span>.<span style="color:#a6e22e">message</span>);
  }
}

<span style="color:#a6e22e">run</span>();
</code></pre></div><p><code>.github/workflows/issue.yml</code></p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-yml" data-lang="yml"><span style="color:#66d9ef">name</span>: <span style="color:#e6db74">&#34;On issue&#34;</span>
<span style="color:#66d9ef">on</span>:
  <span style="color:#66d9ef">issue</span>:
    <span style="color:#66d9ef">types</span>: [opened, reopened, edited]
<span style="color:#66d9ef">jobs</span>:
  <span style="color:#66d9ef">build</span>:
    <span style="color:#66d9ef">runs-on</span>: ubuntu-latest
    <span style="color:#66d9ef">steps</span>:
      - <span style="color:#66d9ef">uses</span>: actions/checkout@v2
      - <span style="color:#66d9ef">uses</span>: ./
</code></pre></div><p><code>npm run all</code> 打包, 这一步很重要, 不然 dist 不会生效, 可以考虑使用 git hooks 来做</p>
<p>然后是 push 代码, 接着 新建一个 issue 来触发一下</p>
<p><!-- raw HTML omitted --></p>
<p><code>issue</code> 报错了, 说不是合法的 event name. 好吧, 这里需要修改为 issues, 我们重新提交一下, 然后再触发它. 因为这里有 edited 可以触发, 我们修改一下 issue 的内容, 然后重新 commit</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-yaml" data-lang="yaml"><span style="color:#66d9ef">name</span>: <span style="color:#e6db74">&#34;On issue&#34;</span>
<span style="color:#66d9ef">on</span>:
  <span style="color:#66d9ef">issues</span>:
    <span style="color:#66d9ef">types</span>: [opened, reopened, edited]
<span style="color:#66d9ef">jobs</span>:
  <span style="color:#66d9ef">build</span>:
    <span style="color:#66d9ef">runs-on</span>: ubuntu-latest
    <span style="color:#66d9ef">steps</span>:
      - <span style="color:#66d9ef">uses</span>: actions/checkout@v2
      - <span style="color:#66d9ef">uses</span>: ./
</code></pre></div><p><!-- raw HTML omitted --></p>
<p>这次, 成功触发了 action, 并且输出了 Hello world.</p>
<h3 id="actionyml-配置">action.yml 配置</h3>
<p>前面说过, 这个文件是 action 的配置文件(或者可以叫清单文件), 其中有一些配置选项</p>
<p>在 actions 中可以配置参数, 以便于从外部传入, 默认的</p>
<p>默认的文件内容如下:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-yaml" data-lang="yaml"><span style="color:#66d9ef">name</span>: <span style="color:#e6db74">&#34;Your name here&#34;</span> <span style="color:#75715e"># 顾名思义, action的名字</span>
<span style="color:#66d9ef">description</span>: <span style="color:#e6db74">&#34;Provide a description here&#34;</span> <span style="color:#75715e"># 对于action的说明</span>
<span style="color:#66d9ef">author</span>: <span style="color:#e6db74">&#34;Your name or organization here&#34;</span> <span style="color:#75715e"># 作者名/组织名/email 之类的信息</span>
<span style="color:#66d9ef">inputs</span>: <span style="color:#75715e"># 参数的字典</span>
  <span style="color:#66d9ef">milliseconds</span>: <span style="color:#75715e"># change this # 参数名,</span>
    <span style="color:#66d9ef">required</span>: <span style="color:#66d9ef">true</span> <span style="color:#75715e"># 是否是必填</span>
    <span style="color:#66d9ef">description</span>: <span style="color:#e6db74">&#34;input description here&#34;</span> <span style="color:#75715e"># 参数的说明</span>
    <span style="color:#66d9ef">default</span>: <span style="color:#e6db74">&#34;default value if applicable&#34;</span> <span style="color:#75715e"># 默认值</span>
<span style="color:#66d9ef">runs</span>: <span style="color:#75715e"># 运行的环境</span>
  <span style="color:#66d9ef">using</span>: <span style="color:#e6db74">&#34;node12&#34;</span> <span style="color:#75715e"># 运行环境为 node12</span>
  <span style="color:#66d9ef">main</span>: <span style="color:#e6db74">&#34;dist/index.js&#34;</span> <span style="color:#75715e"># 入口文件, 就是这个东西要求我们必须编译ts为js后才可用</span>
</code></pre></div><p>看过了默认文件内容后, 我们要开始尝试修改了<a href="https://docs.github.com/en/actions/creating-actions/metadata-syntax-for-github-actions">(文档在这里)</a>, 我们通过文档得知, 有如下的配置参数</p>
<ul>
<li><a href="https://docs.github.com/en/actions/creating-actions/metadata-syntax-for-github-actions#name"><code>name</code></a></li>
<li><a href="https://docs.github.com/en/actions/creating-actions/metadata-syntax-for-github-actions#author"><code>author</code></a></li>
<li><a href="https://docs.github.com/en/actions/creating-actions/metadata-syntax-for-github-actions#description"><code>description</code></a></li>
<li><a href="https://docs.github.com/en/actions/creating-actions/metadata-syntax-for-github-actions#inputs"><code>inputs</code></a></li>
<li><a href="https://docs.github.com/en/actions/creating-actions/metadata-syntax-for-github-actions#outputs"><code>outputs</code></a></li>
<li><a href="https://docs.github.com/en/actions/creating-actions/metadata-syntax-for-github-actions#outputs-for-composite-run-steps-actions"><code>outputs</code> for composite run steps actions</a></li>
<li><a href="https://docs.github.com/en/actions/creating-actions/metadata-syntax-for-github-actions#runs-for-javascript-actions"><code>runs</code> for JavaScript actions</a></li>
<li><a href="https://docs.github.com/en/actions/creating-actions/metadata-syntax-for-github-actions#runs-for-composite-run-steps-actions"><code>runs</code> for composite run steps actions</a></li>
<li><a href="https://docs.github.com/en/actions/creating-actions/metadata-syntax-for-github-actions#runs-for-docker-actions"><code>runs</code> for Docker actions</a></li>
<li><a href="https://docs.github.com/en/actions/creating-actions/metadata-syntax-for-github-actions#branding"><code>branding</code></a></li>
</ul>
<p>在配置中没有出现的 2 个参数</p>
<ol>
<li>outputs: 输出参数, 因为各个 action 之间其实互相是不知道的, 用这个, 可以做到约定式输出, 比如我在 actions 1 里执行了某个东西, 并将其中计算的结果放到这个参数内, 后面就可以用了, 可以简单理解为 action 的返回值</li>
<li>branding: action 对应的徽章样式, 是在<a href="https://github.com/marketplace?type=actions">GitHub Marketplace</a>里的样子</li>
</ol>
<p>我们知道 runs 支持三种形式</p>
<ol>
<li>js(本篇就用的这个)</li>
<li>composite: 复合式, 其实就是使用 linux 命令(当然如果是 macos 设备, 理论上也支持), shell 脚本</li>
<li>Docker: 使用 docker 环境，优点就不多说了, 配置方便, 普适性较强, 缺点是没有 js 和 composite 快, 毕竟加载 docker 需要时间, 镜像越大速度越慢</li>
</ol>
<p>inputs 有一个需要注意的点: 在 js 代码里获取的时候, 使用原名称即可, 但如果你是在 shell 里使用(composite, 或其他语言, 比如 docker 使用 c 语言或者 java 等等), 则需要通过 <code>INPUT_&lt;VARIABLE_NAME&gt;</code>的名称在环境变量里获取</p>
<p>简单的概念完成了, 接着我们就来实战一下</p>
<h3 id="环境变量">环境变量</h3>
<p>环境变量就是你在配置自己的工作流时, 可以使用 <code>$ENV_VAR</code>这种方式来使用环境变量, 至于来源, 看<a href="https://docs.github.com/en/actions/configuring-and-managing-workflows/using-environment-variables#default-environment-variables">github 默认的环境变量</a>, 包括但不仅限于<code>$HOME</code>,<code>$GITHUB_WORKSPACE</code>之类的, 具体看官方文档</p>
<h3 id="配置敏感信息的问题">配置敏感信息的问题</h3>
<p>我们都知道, 很多情况下, 项目有一些隐秘信息, 不能直接配置在项目内, 包括但不仅限于:</p>
<ul>
<li>github token</li>
<li>各种账号的用户名密码</li>
<li>私钥信息</li>
<li>各种网站的 api key,app key, secret key 等等</li>
</ul>
<p>这时候, 就需要有一些技巧来配置它们, 并在代码中读取, <a href="https://docs.github.com/en/actions/configuring-and-managing-workflows/using-variables-and-secrets-in-a-workflow">官方文档</a></p>
<h4 id="配置">配置</h4>
<p>这一步是在 github 仓库的 setting 里完成的</p>
<p><!-- raw HTML omitted --></p>
<p><!-- raw HTML omitted --></p>
<p><!-- raw HTML omitted --></p>
<p><img src="https://cdn.jsdelivr.net/gh/kikt-blog/blog-2@t1/uPic/1599525481.png" alt="image-20200908083801557"></p>
<p>这里看到, 我们虽然用的是<strong>小写</strong>, 但是实际上写入的时候会是<strong>大写</strong>, 这里需要注意一下</p>
<h4 id="读取">读取</h4>
<p>这个读取的过程并不是在 js 代码中, 而是在 yml 中配置, 配置成 inputs 的值,既然需要值, 就需要对于的预配置, 然后通过 <code>${{secrets.&lt;VAR_NAME&gt; }}</code>的方式来获取</p>
<ol>
<li>先定义一个选项以便于外部知道, 我们需要这个, 反应到项目中就是<code>action.yml</code></li>
</ol>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-yml" data-lang="yml"><span style="color:#66d9ef">name</span>: <span style="color:#e6db74">&#34;Auto label&#34;</span>
<span style="color:#66d9ef">description</span>: <span style="color:#e6db74">&#34;Automation generate label for issues.&#34;</span>
<span style="color:#66d9ef">author</span>: <span style="color:#e6db74">&#34;Caijinglong&#34;</span>
<span style="color:#66d9ef">inputs</span>:
  <span style="color:#66d9ef">user_name</span>:
    <span style="color:#66d9ef">required</span>: <span style="color:#66d9ef">true</span>
    <span style="color:#66d9ef">description</span>: <span style="color:#e6db74">&#34;User name&#34;</span>
<span style="color:#66d9ef">runs</span>:
  <span style="color:#66d9ef">using</span>: <span style="color:#e6db74">&#34;node12&#34;</span>
  <span style="color:#66d9ef">main</span>: <span style="color:#e6db74">&#34;dist/index.js&#34;</span>
</code></pre></div><ol start="2">
<li>
<p>配置 workflow: <code>.github/workflows/issue.yml</code></p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-yml" data-lang="yml"><span style="color:#66d9ef">name</span>: <span style="color:#e6db74">&#34;On issue&#34;</span>
<span style="color:#66d9ef">on</span>:
  <span style="color:#66d9ef">issues</span>:
    <span style="color:#66d9ef">types</span>: [opened, reopened, edited]
<span style="color:#66d9ef">jobs</span>:
  <span style="color:#66d9ef">build</span>:
    <span style="color:#66d9ef">runs-on</span>: ubuntu-latest
    <span style="color:#66d9ef">steps</span>:
      - <span style="color:#66d9ef">uses</span>: actions/checkout@v2
      - <span style="color:#66d9ef">uses</span>: ./
        <span style="color:#66d9ef">with</span>:
          <span style="color:#66d9ef">user_name</span>: ${{ secrets.USER_NAME }}
</code></pre></div></li>
</ol>
<h4 id="测试下">测试下</h4>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-ts" data-lang="ts"><span style="color:#66d9ef">import</span> <span style="color:#f92672">*</span> <span style="color:#66d9ef">as</span> <span style="color:#a6e22e">core</span> <span style="color:#a6e22e">from</span> <span style="color:#e6db74">&#34;@actions/core&#34;</span>;

<span style="color:#a6e22e">async</span> <span style="color:#66d9ef">function</span> <span style="color:#a6e22e">run</span>()<span style="color:#f92672">:</span> <span style="color:#a6e22e">Promise</span>&lt;<span style="color:#f92672">void</span>&gt; {
  <span style="color:#66d9ef">try</span> {
    <span style="color:#a6e22e">core</span>.<span style="color:#a6e22e">info</span>(<span style="color:#e6db74">`Hello world`</span>);
    <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">username</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">core</span>.<span style="color:#a6e22e">getInput</span>(<span style="color:#e6db74">&#34;user_name&#34;</span>);
    <span style="color:#a6e22e">core</span>.<span style="color:#a6e22e">info</span>(<span style="color:#e6db74">`Hello </span><span style="color:#e6db74">${</span><span style="color:#a6e22e">username</span><span style="color:#e6db74">}</span><span style="color:#e6db74">`</span>);

    <span style="color:#a6e22e">core</span>.<span style="color:#a6e22e">info</span>(<span style="color:#e6db74">`username === admin : </span><span style="color:#e6db74">${</span><span style="color:#a6e22e">username</span> <span style="color:#f92672">===</span> <span style="color:#e6db74">&#34;admin&#34;</span><span style="color:#e6db74">}</span><span style="color:#e6db74">`</span>);
  } <span style="color:#66d9ef">catch</span> (<span style="color:#a6e22e">error</span>) {
    <span style="color:#a6e22e">core</span>.<span style="color:#a6e22e">setFailed</span>(<span style="color:#a6e22e">error</span>.<span style="color:#a6e22e">message</span>);
  }
}

<span style="color:#a6e22e">run</span>();
</code></pre></div><p>经常 push, 老要修改东西, 很麻烦, 简单些个推送脚本</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-shell" data-lang="shell">touch push.sh
chmod +x push.sh
echo <span style="color:#e6db74">&#34;npm run all &amp;&amp; git add . &amp;&amp; git commit -m &#39;push with shell&#39; &amp;&amp; git push&#34;</span> &gt; push.sh

./push.sh
</code></pre></div><p>然后就是使用 open issue 的方式触发了</p>
<p><img src="https://cdn.jsdelivr.net/gh/kikt-blog/blog-2@t1/uPic/1599531146.png" alt="image-20200908101226112"></p>
<p>然后, 嗯, 结果是这样的, 这里的*** 就是被&rsquo;安全化&rsquo;过的, 鉴于我们 admin 是手输入的, 但是'&lsquo;碰巧'&lsquo;和 secret 里配置的一样, 所以一起被打码了, 然后, 结果是 true, 说明吧, 虽然这里被打码了, 但是并不影响真实的运行结果</p>
<p>前面简单的入门配置都完成了, 接下来简单的实战一下</p>
<h2 id="实战">实战</h2>
<p>本篇的 action 项目是自动根据 issue 标题决定添加 issue label</p>
<h3 id="使用-github-api">使用 github api</h3>
<p>学习下如何使用 api, 这里使用<code>@actions/github</code>提供的能力</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-ts" data-lang="ts"><span style="color:#66d9ef">import</span> <span style="color:#f92672">*</span> <span style="color:#66d9ef">as</span> <span style="color:#a6e22e">github</span> <span style="color:#a6e22e">from</span> <span style="color:#e6db74">&#39;@actions/github&#39;</span>

...
<span style="color:#a6e22e">core</span>.<span style="color:#a6e22e">info</span>(<span style="color:#e6db74">`event name = </span><span style="color:#e6db74">${</span><span style="color:#a6e22e">github</span>.<span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">eventName</span><span style="color:#e6db74">}</span><span style="color:#e6db74">`</span>)

</code></pre></div><p><img src="https://cdn.jsdelivr.net/gh/kikt-blog/blog-2@t1/uPic/1599531623.png" alt="image-20200908102023621"></p>
<p>结果就是这样</p>
<h3 id="github-配置-label">github 配置 label</h3>
<p>先思考步骤</p>
<ol>
<li>获取所有的 label</li>
<li>匹配 issue 标题, 使用正则获取开头的<code>[]</code>内的内容如<code>[bug]</code> 标题的, 自动标注 bug label, feature/feature request 之类的自动标注 feature, 有就创建, 没有就不管</li>
</ol>
<p>核心代码:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-ts" data-lang="ts"><span style="color:#66d9ef">import</span> <span style="color:#f92672">*</span> <span style="color:#66d9ef">as</span> <span style="color:#a6e22e">core</span> <span style="color:#a6e22e">from</span> <span style="color:#e6db74">&#34;@actions/core&#34;</span>;
<span style="color:#66d9ef">import</span> <span style="color:#f92672">*</span> <span style="color:#66d9ef">as</span> <span style="color:#a6e22e">github</span> <span style="color:#a6e22e">from</span> <span style="color:#e6db74">&#34;@actions/github&#34;</span>;
<span style="color:#66d9ef">import</span> <span style="color:#f92672">*</span> <span style="color:#66d9ef">as</span> <span style="color:#a6e22e">Webhooks</span> <span style="color:#a6e22e">from</span> <span style="color:#e6db74">&#34;@octokit/webhooks&#34;</span>;

<span style="color:#66d9ef">export</span> <span style="color:#a6e22e">async</span> <span style="color:#66d9ef">function</span> <span style="color:#a6e22e">run</span>(<span style="color:#a6e22e">githubToken</span>: <span style="color:#66d9ef">string</span>)<span style="color:#f92672">:</span> <span style="color:#a6e22e">Promise</span>&lt;<span style="color:#f92672">void</span>&gt; {
  <span style="color:#66d9ef">try</span> {
    <span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">github</span>.<span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">eventName</span> <span style="color:#f92672">!==</span> <span style="color:#e6db74">&#34;issues&#34;</span>) {
      <span style="color:#a6e22e">core</span>.<span style="color:#a6e22e">info</span>(
        <span style="color:#e6db74">`目前仅支持 issues 触发, 你的类型是</span><span style="color:#e6db74">${</span><span style="color:#a6e22e">github</span>.<span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">eventName</span><span style="color:#e6db74">}</span><span style="color:#e6db74">`</span>
      );
      <span style="color:#66d9ef">return</span>;
    }
    <span style="color:#a6e22e">core</span>.<span style="color:#a6e22e">info</span>(<span style="color:#e6db74">`The run token = &#39;</span><span style="color:#e6db74">${</span><span style="color:#a6e22e">githubToken</span><span style="color:#e6db74">}</span><span style="color:#e6db74">&#39;`</span>);

    <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">payload</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">github</span>.<span style="color:#a6e22e">context</span>
      .<span style="color:#a6e22e">payload</span> <span style="color:#66d9ef">as</span> <span style="color:#a6e22e">Webhooks</span>.<span style="color:#a6e22e">EventPayloads</span>.<span style="color:#a6e22e">WebhookPayloadIssues</span>;

    <span style="color:#a6e22e">core</span>.<span style="color:#a6e22e">info</span>(<span style="color:#e6db74">`Hello world`</span>);
    <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">username</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">core</span>.<span style="color:#a6e22e">getInput</span>(<span style="color:#e6db74">&#34;user_name&#34;</span>);
    <span style="color:#a6e22e">core</span>.<span style="color:#a6e22e">info</span>(<span style="color:#e6db74">`Hello </span><span style="color:#e6db74">${</span><span style="color:#a6e22e">username</span><span style="color:#e6db74">}</span><span style="color:#e6db74">`</span>);

    <span style="color:#a6e22e">core</span>.<span style="color:#a6e22e">info</span>(<span style="color:#e6db74">`username === admin : </span><span style="color:#e6db74">${</span><span style="color:#a6e22e">username</span> <span style="color:#f92672">===</span> <span style="color:#e6db74">&#34;admin&#34;</span><span style="color:#e6db74">}</span><span style="color:#e6db74">`</span>);

    <span style="color:#a6e22e">core</span>.<span style="color:#a6e22e">info</span>(<span style="color:#e6db74">`event name = </span><span style="color:#e6db74">${</span><span style="color:#a6e22e">github</span>.<span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">eventName</span><span style="color:#e6db74">}</span><span style="color:#e6db74">`</span>);

    <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">octokit</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">github</span>.<span style="color:#a6e22e">getOctokit</span>(<span style="color:#a6e22e">githubToken</span>);

    <span style="color:#66d9ef">const</span> { <span style="color:#a6e22e">owner</span>, <span style="color:#a6e22e">repo</span> } <span style="color:#f92672">=</span> <span style="color:#a6e22e">github</span>.<span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">repo</span>;
    <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">issue_number</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">payload</span>.<span style="color:#a6e22e">issue</span>.<span style="color:#66d9ef">number</span>;
    <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">regex</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">/\[([^\]]+)\]/g</span>;
    <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">array</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">regex</span>.<span style="color:#a6e22e">exec</span>(<span style="color:#a6e22e">payload</span>.<span style="color:#a6e22e">issue</span>.<span style="color:#a6e22e">title</span>);

    <span style="color:#a6e22e">core</span>.<span style="color:#a6e22e">info</span>(
      <span style="color:#e6db74">`触发的issue : owner: </span><span style="color:#e6db74">${</span><span style="color:#a6e22e">owner</span><span style="color:#e6db74">}</span><span style="color:#e6db74">, repo = </span><span style="color:#e6db74">${</span><span style="color:#a6e22e">repo</span><span style="color:#e6db74">}</span><span style="color:#e6db74">, issue_number = </span><span style="color:#e6db74">${</span><span style="color:#a6e22e">issue_number</span><span style="color:#e6db74">}</span><span style="color:#e6db74">`</span>
    );

    <span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">array</span> <span style="color:#f92672">==</span> <span style="color:#66d9ef">null</span>) {
      <span style="color:#a6e22e">core</span>.<span style="color:#a6e22e">info</span>(<span style="color:#e6db74">`没有找到标签, 回复一下`</span>);
      <span style="color:#a6e22e">await</span> <span style="color:#a6e22e">octokit</span>.<span style="color:#a6e22e">issues</span>.<span style="color:#a6e22e">createComment</span>({
        <span style="color:#a6e22e">owner</span>,
        <span style="color:#a6e22e">repo</span>,
        <span style="color:#a6e22e">issue_number</span>,
        <span style="color:#a6e22e">body</span><span style="color:#f92672">:</span> <span style="color:#e6db74">`没有找到[xxx]类型的标签`</span>,
      });
      <span style="color:#66d9ef">return</span>;
    }

    <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">labelName</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">array</span>[<span style="color:#ae81ff">1</span>];
    <span style="color:#a6e22e">core</span>.<span style="color:#a6e22e">info</span>(<span style="color:#e6db74">`预计的标签名: labelname is = </span><span style="color:#e6db74">${</span><span style="color:#a6e22e">labelName</span><span style="color:#e6db74">}</span><span style="color:#e6db74">`</span>);

    <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">allLabels</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">await</span> <span style="color:#a6e22e">octokit</span>.<span style="color:#a6e22e">issues</span>.<span style="color:#a6e22e">listLabelsForRepo</span>({
      <span style="color:#a6e22e">owner</span>,
      <span style="color:#a6e22e">repo</span>,
    });

    <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">labelText</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">allLabels</span>.<span style="color:#a6e22e">data</span>
      .<span style="color:#a6e22e">map</span>&lt;<span style="color:#f92672">string</span>&gt;((<span style="color:#a6e22e">data</span>) <span style="color:#f92672">=&gt;</span> {
        <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">data</span>.<span style="color:#a6e22e">name</span>;
      })
      .<span style="color:#a6e22e">join</span>(<span style="color:#e6db74">&#34;,&#34;</span>);

    <span style="color:#a6e22e">core</span>.<span style="color:#a6e22e">info</span>(<span style="color:#e6db74">`找到了一堆标签 </span><span style="color:#e6db74">${</span><span style="color:#a6e22e">labelText</span><span style="color:#e6db74">}</span><span style="color:#e6db74">`</span>);

    <span style="color:#66d9ef">let</span> <span style="color:#a6e22e">haveResult</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">false</span>;

    <span style="color:#66d9ef">for</span> (<span style="color:#66d9ef">const</span> <span style="color:#a6e22e">label</span> <span style="color:#a6e22e">of</span> <span style="color:#a6e22e">allLabels</span>.<span style="color:#a6e22e">data</span>) {
      <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">labels</span> <span style="color:#f92672">=</span> [<span style="color:#a6e22e">label</span>.<span style="color:#a6e22e">name</span>];
      <span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">labelName</span>.<span style="color:#a6e22e">toUpperCase</span>() <span style="color:#f92672">===</span> <span style="color:#a6e22e">label</span>.<span style="color:#a6e22e">name</span>.<span style="color:#a6e22e">toUpperCase</span>()) {
        <span style="color:#a6e22e">core</span>.<span style="color:#a6e22e">info</span>(<span style="color:#e6db74">&#34;找到了标签, 标上&#34;</span>);
        <span style="color:#a6e22e">await</span> <span style="color:#a6e22e">octokit</span>.<span style="color:#a6e22e">issues</span>.<span style="color:#a6e22e">addLabels</span>({
          <span style="color:#a6e22e">owner</span>,
          <span style="color:#a6e22e">repo</span>,
          <span style="color:#a6e22e">issue_number</span>,
          <span style="color:#a6e22e">labels</span>,
        });
        <span style="color:#a6e22e">haveResult</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>;
        <span style="color:#66d9ef">break</span>;
      }
    }

    <span style="color:#66d9ef">if</span> (<span style="color:#f92672">!</span><span style="color:#a6e22e">haveResult</span>) {
      <span style="color:#a6e22e">core</span>.<span style="color:#a6e22e">info</span>(
        <span style="color:#e6db74">`没找到标签 </span><span style="color:#e6db74">${</span><span style="color:#a6e22e">labelName</span><span style="color:#e6db74">}</span><span style="color:#e6db74">, 回复下, 可能是新问题, 现在先短暂回复一下`</span>
      );
      <span style="color:#a6e22e">await</span> <span style="color:#a6e22e">octokit</span>.<span style="color:#a6e22e">issues</span>.<span style="color:#a6e22e">createComment</span>({
        <span style="color:#a6e22e">owner</span>,
        <span style="color:#a6e22e">repo</span>,
        <span style="color:#a6e22e">issue_number</span>,
        <span style="color:#a6e22e">body</span><span style="color:#f92672">:</span> <span style="color:#e6db74">`没有找到 </span><span style="color:#e6db74">${</span><span style="color:#a6e22e">labelName</span><span style="color:#e6db74">}</span><span style="color:#e6db74">`</span>,
      });
    }

    <span style="color:#a6e22e">core</span>.<span style="color:#a6e22e">info</span>(<span style="color:#e6db74">&#34;run success&#34;</span>);
  } <span style="color:#66d9ef">catch</span> (<span style="color:#a6e22e">error</span>) {
    <span style="color:#a6e22e">core</span>.<span style="color:#a6e22e">error</span>(<span style="color:#e6db74">&#34;The action run error:&#34;</span>);
    <span style="color:#a6e22e">core</span>.<span style="color:#a6e22e">error</span>(<span style="color:#a6e22e">error</span>);
    <span style="color:#a6e22e">core</span>.<span style="color:#a6e22e">setFailed</span>(<span style="color:#a6e22e">error</span>.<span style="color:#a6e22e">message</span>);
  }
}
</code></pre></div><p>配置文件</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-yaml" data-lang="yaml"><span style="color:#66d9ef">name</span>: <span style="color:#e6db74">&#34;On issue&#34;</span>
<span style="color:#66d9ef">on</span>:
  <span style="color:#66d9ef">issues</span>:
    <span style="color:#66d9ef">types</span>: [opened, reopened, edited]
<span style="color:#66d9ef">jobs</span>:
  <span style="color:#66d9ef">build</span>:
    <span style="color:#66d9ef">runs-on</span>: ubuntu-latest
    <span style="color:#66d9ef">steps</span>:
      - <span style="color:#66d9ef">uses</span>: actions/checkout@v2
      - <span style="color:#66d9ef">uses</span>: ./
        <span style="color:#66d9ef">with</span>:
          <span style="color:#66d9ef">user_name</span>: ${{ secrets.USER_NAME }}
          <span style="color:#66d9ef">github-token</span>: ${{ secrets.GITHUB_TOKEN }}
</code></pre></div><p>在编译上传后看一下</p>
<p><!-- raw HTML omitted --></p>
<p><!-- raw HTML omitted --></p>
<p><!-- raw HTML omitted --></p>
<p>在经过调试后, 达到了预期的效果, 找到了就标记上, 没有就不标</p>
<hr>
<p>也就是说, 在经历过这些以后, 就可以简单的达到我们的目的,后续的话, 可以根据需求扩展功能, 目前的瑕疵是, 部分功能调试起来并不方便</p>
<p>在实际使用时为了单元测试的方便, 可以封装的更加细一些. 比如: 把,github token, issue, repo, owner, title 等参数全部抽出去, 以便于本地测试是否真的有用</p>
<h2 id="发布">发布</h2>
<p>写完了, 要发布了, 也就是让别人可以在 <a href="https://github.com/marketplace?type=actions">action 商店</a> 里搜到你的作品</p>
<p>一般来讲有如下三个步骤</p>
<ol>
<li>写 README</li>
<li>打 tag/release</li>
<li>发布到 action 商店里</li>
</ol>
<h3 id="最终的文件样式">最终的文件样式</h3>
<pre><code class="language-treeview" data-lang="treeview">.
├── LICENSE
├── README.md
├── __tests__/
│   └── main.test.ts
├── action.yml
├── dist/
│   ├── index.js
│   ├── index.js.map
│   ├── licenses.txt
│   └── sourcemap-register.js
├── jest.config.js
├── lib/
│   ├── handle.js
│   ├── main.js
│   └── wait.js
├── package-lock.json
├── package.json
├── push.sh*
├── src/
│   ├── handle.ts
│   ├── main.ts
│   └── wait.ts
└── tsconfig.json
</code></pre><h3 id="编写-readme">编写 README</h3>
<p>这个就不展开说了, 抄一下别人的, 然后自己随便搞搞</p>
<h3 id="打-tag">打 tag</h3>
<p>直接使用 github web 端的 release 功能, 这样可以同时完成 tag 和 release 的, 一般来说, action 比较常见的是 1 位长度的 action, 我们直接打一个 v1.0.0, 然后使用者的话, 一般使用 xxx@v1 就可以了</p>
<p>比如最常用的<code> actions/checkout</code>, 目前最新 release 版本是<code>v2.3.2</code>, 但是你可以直接使用@v2 来使用一样</p>
<p><a href="https://github.com/actions/toolkit/blob/master/docs/action-versioning.md">官方说明</a>, 使用时可以接受诸如<code>v1</code> <code>v1.0.0</code> <code>commitHash</code>, <code>master</code> 这样的标记, 但, 一般不建议使用@master</p>
<h3 id="发布吧">发布吧</h3>
<p><a href="https://github.com/marketplace/new">网址在这</a>, 选中你的 action, 这个名字是你定义在<code>action.yml</code>里的</p>
<p><img src="https://cdn.jsdelivr.net/gh/kikt-blog/blog-2@t1/uPic/1599621342.png" alt="image-20200909111541661"></p>
<p><img src="https://cdn.jsdelivr.net/gh/kikt-blog/blog-2@t1/uPic/1599621484.png" alt="image-20200909111804111"></p>
<p>提示, 需要 release, 这里就来一个 v1.0.0 吧</p>
<p>当公开仓库后, 就可以看到这里多了一个 release action 的选项</p>
<p><img src="https://cdn.jsdelivr.net/gh/kikt-blog/blog-2@t1/uPic/1599624120.png" alt="image-20200909120200386"></p>
<p>然后, 如果你是第一次使用, 可能有两个额外步骤</p>
<ol>
<li>发布的协议</li>
<li>要求必须开启两步验证, 我这里使用<a href="https://authy.com/">authy</a> , 你可以使用别的任何 github 支持的工具, 具体的过程可以百度一下</li>
</ol>
<p><img src="https://cdn.jsdelivr.net/gh/kikt-blog/blog-2@t1/uPic/1599624330.png" alt="image-20200909120530155"></p>
<p>提示重名了, 我们修改一下 action.yml , 接着就可以用了</p>
<h2 id="后记">后记</h2>
<p>本篇结合了 github 文档和模板完成了 github action 的创建, 使用, 调用的过程 <a href="https://github.com/CaiJingLong/action_auto_label">仓库</a></p>

                        </div>

                        

<div class="post-archive">
    <h2>See Also</h2>
    <ul class="listing">
        
        <li><a href="/posts/flutter/dart/generators/">dart中的生成器函数</a></li>
        
        <li><a href="/posts/flutter/jenkins&#43;fastlane/">flutter Jenkins&#43;fastlane 自动化打测试包, 并上传蒲公英</a></li>
        
        <li><a href="/posts/flutter/focusnode-2/">Flutter FocusNode 焦点那点事-(二)</a></li>
        
        <li><a href="/posts/flutter/focusnode-1/">Flutter FocusNode 焦点那点事-(一)</a></li>
        
        <li><a href="/posts/flutter/route/navigator-helper2/">Navigator Helper2</a></li>
        
    </ul>
</div>


                        <div class="post-meta meta-tags">
                            
                            <ul class="clearfix">
                                
                                <li><a href="http://www.kikt.top/tags/flutter">flutter</a></li>
                                
                                <li><a href="http://www.kikt.top/tags/actions">actions</a></li>
                                
                            </ul>
                            
                        </div>
                    </article>
                    
    

    
    
    <div class="post bg-white">
      <script src="https://utteranc.es/client.js"
            repo= "caijinglong/kikt-blog-comment"
            issue-term="pathname"
            theme="github-light"
            crossorigin="anonymous"
            async>
      </script>
    </div>
    
                </div>
            </div>
            <div id="secondary">
    <section class="widget">
        <form id="search" action="//www.google.com/search" method="get" accept-charset="utf-8" target="_blank" _lpchecked="1">
      
      <input type="text" name="q" maxlength="20" placeholder="Search">
      <input type="hidden" name="sitesearch" value="http://www.kikt.top">
      <button type="submit" class="submit icon-search"></button>
</form>
    </section>
    
    <section class="widget">
        <h3 class="widget-title">最近文章</h3>
<ul class="widget-list">
    
    <li>
        <a href="http://www.kikt.top/posts/java/crack/crack1/" title="使用javassist,修改jar包方法实现">使用javassist,修改jar包方法实现</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/posts/github/actions/create/" title="Github action 的开发到发布">Github action 的开发到发布</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/posts/flutter/jenkins&#43;fastlane/" title="flutter Jenkins&#43;fastlane 自动化打测试包, 并上传蒲公英">flutter Jenkins&#43;fastlane 自动化打测试包, 并上传蒲公英</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/posts/go/compile-for-android/" title="编译 go 源码为 android 动态库(so)">编译 go 源码为 android 动态库(so)</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/posts/flutter/focusnode-2/" title="Flutter FocusNode 焦点那点事-(二)">Flutter FocusNode 焦点那点事-(二)</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/posts/flutter/focusnode-1/" title="Flutter FocusNode 焦点那点事-(一)">Flutter FocusNode 焦点那点事-(一)</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/posts/server/caddy/cros_for_local_dev/" title="用 Caddy 解决 web 开发中本地跨域的问题">用 Caddy 解决 web 开发中本地跨域的问题</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/posts/flutter/route/navigator-helper2/" title="Navigator Helper2">Navigator Helper2</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/posts/flutter/grey-app/" title="flutter 怎么实现app整体灰度">flutter 怎么实现app整体灰度</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/posts/flutter/plugin/flutter-sdk-import-aar/" title="Flutter 插件开发之引入aar到安卓部分 并使用本地maven">Flutter 插件开发之引入aar到安卓部分 并使用本地maven</a>
    </li>
    
</ul>
    </section>

    

    <section class="widget">
        <h3 class="widget-title">分类</h3>
<ul class="widget-list">
    
    <li>
        <a href="http://www.kikt.top/categories/android/">android(25)</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/categories/caddy/">caddy(1)</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/categories/dart/">dart(5)</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/categories/docker/">docker(3)</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/categories/flutter/">flutter(60)</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/categories/github/">github(3)</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/categories/go/">go(1)</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/categories/golang/">golang(1)</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/categories/iOS/">iOS(6)</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/categories/ios/">ios(1)</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/categories/java/">java(6)</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/categories/jetbrains/">jetbrains(1)</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/categories/other/">other(3)</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/categories/python/">python(1)</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/categories/server/">server(5)</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/categories/spring/">spring(3)</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/categories/tools/">tools(1)</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/categories/%E6%9D%82%E9%A1%B9/">杂项(1)</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/categories/%E6%B6%82%E9%B8%A6/">涂鸦(1)</a>
    </li>
    
</ul>
    </section>

    <section class="widget">
        <h3 class="widget-title">标签</h3>
<div class="tagcloud">
    
    <a href="http://www.kikt.top/tags/10/">10</a>
    
    <a href="http://www.kikt.top/tags/2.3/">2.3</a>
    
    <a href="http://www.kikt.top/tags/Clipboard/">Clipboard</a>
    
    <a href="http://www.kikt.top/tags/Javassist/">Javassist</a>
    
    <a href="http://www.kikt.top/tags/Migrate/">Migrate</a>
    
    <a href="http://www.kikt.top/tags/aar/">aar</a>
    
    <a href="http://www.kikt.top/tags/actions/">actions</a>
    
    <a href="http://www.kikt.top/tags/aidl/">aidl</a>
    
    <a href="http://www.kikt.top/tags/android/">android</a>
    
    <a href="http://www.kikt.top/tags/androidQ/">androidQ</a>
    
    <a href="http://www.kikt.top/tags/androidX/">androidX</a>
    
    <a href="http://www.kikt.top/tags/apk/">apk</a>
    
    <a href="http://www.kikt.top/tags/app/">app</a>
    
    <a href="http://www.kikt.top/tags/appcode/">appcode</a>
    
    <a href="http://www.kikt.top/tags/azure/">azure</a>
    
    <a href="http://www.kikt.top/tags/ble/">ble</a>
    
    <a href="http://www.kikt.top/tags/bottomsheet/">bottomsheet</a>
    
    <a href="http://www.kikt.top/tags/butterknife/">butterknife</a>
    
    <a href="http://www.kikt.top/tags/c/">c</a>
    
    <a href="http://www.kikt.top/tags/caddy/">caddy</a>
    
    <a href="http://www.kikt.top/tags/camera/">camera</a>
    
    <a href="http://www.kikt.top/tags/cameraX/">cameraX</a>
    
    <a href="http://www.kikt.top/tags/channel/">channel</a>
    
    <a href="http://www.kikt.top/tags/cocoapods/">cocoapods</a>
    
    <a href="http://www.kikt.top/tags/cupertino/">cupertino</a>
    
    <a href="http://www.kikt.top/tags/dart/">dart</a>
    
    <a href="http://www.kikt.top/tags/dart-2.6/">dart-2.6</a>
    
    <a href="http://www.kikt.top/tags/desktop/">desktop</a>
    
    <a href="http://www.kikt.top/tags/dialog/">dialog</a>
    
    <a href="http://www.kikt.top/tags/dmg/">dmg</a>
    
    <a href="http://www.kikt.top/tags/doc/">doc</a>
    
    <a href="http://www.kikt.top/tags/docker/">docker</a>
    
    <a href="http://www.kikt.top/tags/docker-compose/">docker-compose</a>
    
    <a href="http://www.kikt.top/tags/excel/">excel</a>
    
    <a href="http://www.kikt.top/tags/exists/">exists</a>
    
    <a href="http://www.kikt.top/tags/faq/">faq</a>
    
    <a href="http://www.kikt.top/tags/fastlane/">fastlane</a>
    
    <a href="http://www.kikt.top/tags/ffi/">ffi</a>
    
    <a href="http://www.kikt.top/tags/ffmpeg/">ffmpeg</a>
    
    <a href="http://www.kikt.top/tags/flexmark/">flexmark</a>
    
    <a href="http://www.kikt.top/tags/flutter/">flutter</a>
    
    <a href="http://www.kikt.top/tags/flutter-web/">flutter-web</a>
    
    <a href="http://www.kikt.top/tags/focus/">focus</a>
    
    <a href="http://www.kikt.top/tags/focusNode/">focusNode</a>
    
    <a href="http://www.kikt.top/tags/form/">form</a>
    
    <a href="http://www.kikt.top/tags/framework/">framework</a>
    
    <a href="http://www.kikt.top/tags/github/">github</a>
    
    <a href="http://www.kikt.top/tags/go/">go</a>
    
    <a href="http://www.kikt.top/tags/golang/">golang</a>
    
    <a href="http://www.kikt.top/tags/gradle/">gradle</a>
    
    <a href="http://www.kikt.top/tags/http/">http</a>
    
    <a href="http://www.kikt.top/tags/iOS/">iOS</a>
    
    <a href="http://www.kikt.top/tags/ide/">ide</a>
    
    <a href="http://www.kikt.top/tags/idea/">idea</a>
    
    <a href="http://www.kikt.top/tags/index/">index</a>
    
    <a href="http://www.kikt.top/tags/inside/">inside</a>
    
    <a href="http://www.kikt.top/tags/ios/">ios</a>
    
    <a href="http://www.kikt.top/tags/java/">java</a>
    
    <a href="http://www.kikt.top/tags/jenkins/">jenkins</a>
    
    <a href="http://www.kikt.top/tags/jetbrains/">jetbrains</a>
    
    <a href="http://www.kikt.top/tags/jsdelivr/">jsdelivr</a>
    
    <a href="http://www.kikt.top/tags/json/">json</a>
    
    <a href="http://www.kikt.top/tags/json_serializable/">json_serializable</a>
    
    <a href="http://www.kikt.top/tags/junit/">junit</a>
    
    <a href="http://www.kikt.top/tags/library/">library</a>
    
    <a href="http://www.kikt.top/tags/live-template/">live-template</a>
    
    <a href="http://www.kikt.top/tags/loadmore/">loadmore</a>
    
    <a href="http://www.kikt.top/tags/log/">log</a>
    
    <a href="http://www.kikt.top/tags/mac/">mac</a>
    
    <a href="http://www.kikt.top/tags/macos/">macos</a>
    
    <a href="http://www.kikt.top/tags/maven/">maven</a>
    
    <a href="http://www.kikt.top/tags/mysql/">mysql</a>
    
    <a href="http://www.kikt.top/tags/navigationbar/">navigationbar</a>
    
    <a href="http://www.kikt.top/tags/navigator/">navigator</a>
    
    <a href="http://www.kikt.top/tags/ndk/">ndk</a>
    
    <a href="http://www.kikt.top/tags/nginx/">nginx</a>
    
    <a href="http://www.kikt.top/tags/oc/">oc</a>
    
    <a href="http://www.kikt.top/tags/opencv/">opencv</a>
    
    <a href="http://www.kikt.top/tags/poi/">poi</a>
    
    <a href="http://www.kikt.top/tags/pub/">pub</a>
    
    <a href="http://www.kikt.top/tags/python/">python</a>
    
    <a href="http://www.kikt.top/tags/retrofit/">retrofit</a>
    
    <a href="http://www.kikt.top/tags/route/">route</a>
    
    <a href="http://www.kikt.top/tags/server/">server</a>
    
    <a href="http://www.kikt.top/tags/so/">so</a>
    
    <a href="http://www.kikt.top/tags/spp/">spp</a>
    
    <a href="http://www.kikt.top/tags/sprintboot/">sprintboot</a>
    
    <a href="http://www.kikt.top/tags/swift/">swift</a>
    
    <a href="http://www.kikt.top/tags/tap/">tap</a>
    
    <a href="http://www.kikt.top/tags/thymeleaf/">thymeleaf</a>
    
    <a href="http://www.kikt.top/tags/tips/">tips</a>
    
    <a href="http://www.kikt.top/tags/toast/">toast</a>
    
    <a href="http://www.kikt.top/tags/transition/">transition</a>
    
    <a href="http://www.kikt.top/tags/ui/">ui</a>
    
    <a href="http://www.kikt.top/tags/utf8/">utf8</a>
    
    <a href="http://www.kikt.top/tags/yield/">yield</a>
    
    <a href="http://www.kikt.top/tags/%E4%BA%8C%E8%BF%9B%E5%88%B6/">二进制</a>
    
    <a href="http://www.kikt.top/tags/%E4%BA%A4%E5%8F%89/">交叉</a>
    
    <a href="http://www.kikt.top/tags/%E4%BA%A4%E5%8F%89%E7%BC%96%E8%AF%91/">交叉编译</a>
    
    <a href="http://www.kikt.top/tags/%E4%BB%8B%E7%BB%8D/">介绍</a>
    
    <a href="http://www.kikt.top/tags/%E4%BB%A3%E7%A0%81%E8%A7%84%E8%8C%83/">代码规范</a>
    
    <a href="http://www.kikt.top/tags/%E5%86%B2%E7%AA%81/">冲突</a>
    
    <a href="http://www.kikt.top/tags/%E5%8A%A8%E6%80%81/">动态</a>
    
    <a href="http://www.kikt.top/tags/%E5%8F%91%E5%B8%83/">发布</a>
    
    <a href="http://www.kikt.top/tags/%E5%8F%AF%E6%89%A7%E8%A1%8C/">可执行</a>
    
    <a href="http://www.kikt.top/tags/%E5%9B%BE%E5%BA%8A/">图床</a>
    
    <a href="http://www.kikt.top/tags/%E5%9F%8E%E5%B8%82/">城市</a>
    
    <a href="http://www.kikt.top/tags/%E5%A4%A7%E6%96%87%E4%BB%B6/">大文件</a>
    
    <a href="http://www.kikt.top/tags/%E5%B7%A5%E5%85%B7/">工具</a>
    
    <a href="http://www.kikt.top/tags/%E5%B7%B2%E6%9C%89%E9%A1%B9%E7%9B%AE/">已有项目</a>
    
    <a href="http://www.kikt.top/tags/%E5%BC%80%E5%8F%91%E7%8E%AF%E5%A2%83/">开发环境</a>
    
    <a href="http://www.kikt.top/tags/%E6%80%BB%E7%BB%93/">总结</a>
    
    <a href="http://www.kikt.top/tags/%E6%85%A2/">慢</a>
    
    <a href="http://www.kikt.top/tags/%E6%89%93%E5%8C%85/">打包</a>
    
    <a href="http://www.kikt.top/tags/%E6%89%AB%E7%A0%81/">扫码</a>
    
    <a href="http://www.kikt.top/tags/%E6%8F%92%E4%BB%B6/">插件</a>
    
    <a href="http://www.kikt.top/tags/%E6%96%87%E6%A1%A3/">文档</a>
    
    <a href="http://www.kikt.top/tags/%E6%A8%A1%E6%9D%BF/">模板</a>
    
    <a href="http://www.kikt.top/tags/%E6%B8%B2%E6%9F%93/">渲染</a>
    
    <a href="http://www.kikt.top/tags/%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/">源码解析</a>
    
    <a href="http://www.kikt.top/tags/%E7%81%B0%E5%BA%A6/">灰度</a>
    
    <a href="http://www.kikt.top/tags/%E7%82%B9%E5%87%BB/">点击</a>
    
    <a href="http://www.kikt.top/tags/%E7%84%A6%E7%82%B9/">焦点</a>
    
    <a href="http://www.kikt.top/tags/%E7%88%AC%E8%99%AB/">爬虫</a>
    
    <a href="http://www.kikt.top/tags/%E7%8A%B6%E6%80%81%E7%AE%A1%E7%90%86/">状态管理</a>
    
    <a href="http://www.kikt.top/tags/%E7%99%BE%E5%BA%A6%E5%9C%B0%E5%9B%BE/">百度地图</a>
    
    <a href="http://www.kikt.top/tags/%E7%BC%96%E7%A0%81/">编码</a>
    
    <a href="http://www.kikt.top/tags/%E7%BC%96%E7%A8%8B%E6%8A%80%E5%B7%A7/">编程技巧</a>
    
    <a href="http://www.kikt.top/tags/%E7%BC%96%E8%AF%91/">编译</a>
    
    <a href="http://www.kikt.top/tags/%E7%BF%BB%E8%AF%91/">翻译</a>
    
    <a href="http://www.kikt.top/tags/%E8%93%9D%E7%89%99/">蓝牙</a>
    
    <a href="http://www.kikt.top/tags/%E8%A1%A8%E5%8D%95/">表单</a>
    
    <a href="http://www.kikt.top/tags/%E8%B7%AF%E7%94%B1/">路由</a>
    
    <a href="http://www.kikt.top/tags/%E8%BF%87%E5%9C%BA%E5%8A%A8%E7%94%BB/">过场动画</a>
    
    <a href="http://www.kikt.top/tags/%E9%9A%90%E7%A7%81/">隐私</a>
    
</div>
    </section>

    
<section class="widget">
    <h3 class="widget-title">友情链接</h3>
    <ul class="widget-list">
        
        <li>
            <a target="_blank" href="https://github.com/Caijinglong" title="我的Github">我的Github</a>
        </li>
        
        <li>
            <a target="_blank" href="https://flutter.dev" title="flutter 官网">flutter 官网</a>
        </li>
        
    </ul>
</section>


    <section class="widget">
        <h3 class="widget-title">其它</h3>
        <ul class="widget-list">
            <li><a href="http://www.kikt.top/index.xml">文章 RSS</a></li>
        </ul>
    </section>
</div>
        </div>
    </div>
</div>
<footer id="footer">
    <div class="container">
        <div>
            <p>如果我的blog对您有帮助,而您愿意捐赠 😆, 可以用下面的二维码</p>
            <img style="width:5rem;" src="https://dev.azure.com/cjlspy/844861b8-ee45-48a3-b430-b974684baaaa/_apis/git/repositories/bf4da1c1-1cfa-4f54-8a75-745cce2b737c/items?versionDescriptor%5Bversion%5D=master&versionDescriptor%5BversionOptions%5D=0&versionDescriptor%5BversionType%5D=0&resolveLfs=true&%24format=octetStream&api-version=5.0&path=%2F20190424141834.png"/>
            <img style="width:5rem; padding-left: 2rem;" 
            src="https://dev.azure.com/cjlspy/844861b8-ee45-48a3-b430-b974684baaaa/_apis/git/repositories/bf4da1c1-1cfa-4f54-8a75-745cce2b737c/items?versionDescriptor%5Bversion%5D=master&versionDescriptor%5BversionOptions%5D=0&versionDescriptor%5BversionType%5D=0&resolveLfs=true&%24format=octetStream&api-version=5.0&path=%2F20190424141931.png"/>
            <img style="width:5rem; padding-left: 2rem;" 
            src="https://dev.azure.com/cjlspy/844861b8-ee45-48a3-b430-b974684baaaa/_apis/git/repositories/bf4da1c1-1cfa-4f54-8a75-745cce2b737c/items?versionDescriptor%5Bversion%5D=master&versionDescriptor%5BversionOptions%5D=0&versionDescriptor%5BversionType%5D=0&resolveLfs=true&%24format=octetStream&api-version=5.0&path=%2F20190424142013.png"/>
        </div>
        &copy; 2018 <a href="http://www.kikt.top">caijinglong的博客 By caijinglong</a>.
        Powered by <a rel="nofollow noreferer noopener" href="https://gohugo.io" target="_blank">Hugo</a>.
        <a href="http://www.flysnow.org/" target="_blank">Theme</a> based on <a href="https://github.com/rujews/maupassant-hugo" target="_blank">maupassant</a>.<br/>
        本站所有内容基于<a href="https://creativecommons.org/licenses/by-nc-sa/4.0/deed.zh" style="color:coral">CC4.0</a>协议发布,需要转载必须署名
        <br/>
        如果有问题可以<a href="mailto:cjl_spy@163.com">email联系我</a>
        <br/>
        京ICP备18038473号-1
        
    </div>
</footer>


    <script type="text/javascript" src="/js/prism.js" async="true"></script>
    <script type="text/javascript">
    
    (function(){
        $("pre code").parent().addClass("line-numbers")
    }())

    window.MathJax = {
        tex2jax: {
            inlineMath: [ ['$','$'] ],
            processEscapes: true
        }
    };
    </script>
    <script src='https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/MathJax.js?config=TeX-MML-AM_CHTML' async></script>

<a id="rocket" href="#top"></a>
<script type="text/javascript" src="/js/totop.js?v=0.0.0" async=""></script>



<script type="text/javascript" src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js" async></script>




</body>
</html>
